/* TYLER HANN
 * TCH435 
 * 11081310 */

 #include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <signal.h>


#define PORT "9490" /* the port to connect to */
#define BUFSIZE 250 /* the size for the recv buffers */
#define MAXKEYS 100

int sendall(int new_fd, char *msg, int len, int flags,
	const struct sockaddr *to)
{
	/*int send(int sockfd, const void *msg, int len, int flags); 
	send to respond to client
	use the new fd
	set flags to 0
	*/
	socklen_t tolen = sizeof(struct sockaddr_storage);
	int bytes_sent = sendto(new_fd, msg, len, flags, to, tolen);

	if(bytes_sent < 0)
	{
		/* error on the send */
		fprintf(stderr, "errno: %d", errno);
		exit(1);
	}

	/* keep resending until everything makes it through */
	while(bytes_sent < len && bytes_sent >= 0)
	{
		/* not all of the bytes were sent, try sending rest */
		int temp_bytes = sendto(new_fd, &msg[bytes_sent],
			len-bytes_sent, flags, to, tolen);

		if(temp_bytes < 0)
		{
			/* error on the send */
			fprintf(stderr, "errno: %d", errno);
			exit(1);
		}
		else
			bytes_sent += temp_bytes;
	}

	return bytes_sent;
}
			

int main()
{
	int status, s, bytes_sent, len; 
					/* s is our socket fd*/
	int from_size = sizeof(struct sockaddr_storage);
	struct addrinfo hints, *servinfo, *p; /* servinfo will 
								point to the results */
	struct sockaddr_storage recvfrom_addr;

	char recvBuf[BUFSIZE];

	/* 100 key value pairs just stored in arrays for simplicity*/
	char keys[MAXKEYS][20], values[MAXKEYS][200];
	/* used to hold replies */
	char message[260], reply[260*MAXKEYS], *msg;

	int i=0;

	memset(&hints, 0, sizeof hints); // make sure the struct is empty
	hints.ai_family = AF_UNSPEC;     // don't care IPv4 or IPv6
	hints.ai_socktype = SOCK_DGRAM; // TCP stream sockets
	hints.ai_flags = AI_PASSIVE;     // fill in my IP for me
								/*ie bind to ip of host its running on*/

	/* param1 - hostname or ip (NULL if server, ip if client)
	param2 - port to connect on or service (http, ftp, etc)
	param3 - all the addressinfo filled out
	param4 - return (all the server info gets filled in) 
	getaddrinfo returns non-zero on error, and we can print 
	it's error using gai_strerror()
	*/
	if ((status = getaddrinfo(NULL, PORT, &hints, &servinfo)) != 0)
	{
	    fprintf(stderr, "getaddrinfo error: %s\n", gai_strerror(status));
	    exit(1);
	}

	// loop through all the results and connect to the first we can
    for(p = servinfo; p != NULL; p = p->ai_next)
    {
        if ((s = socket(p->ai_family, p->ai_socktype,
                p->ai_protocol)) == -1)
        {
			fprintf(stderr, "errno: %d", errno);
            exit(1);
        }

        if (bind(s, p->ai_addr, p->ai_addrlen) == -1)
        {
        	fprintf(stderr, "errno: %d", errno);
            close(s);
            exit(1);
        }

        break;
    }

    if (p == NULL) {
        fprintf(stderr, "failed to bind socket\n");
        exit(1);
    }

    freeaddrinfo(servinfo);

	/* ready to communicate on socket descriptor s
	*/

	/* int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
	addr is usually a point erto a local struct sockaddr_storage
	will store information about incoming connections (so should be empty)
	addrlen is local int: should be sizeof(struct sockaddr_storage)
	*/


	while(strcmp(recvBuf, "quit") != 0 &&
		strcmp(recvBuf, "exit") != 0) /*main accept loop */
	{
		/* int recv(int sockfd, void *buf, int len, int flags,
            struct sockaddr *from, int *fromlen); 
		buf is the buffer to read info into,
		len is max length of buffer,
		flags can be set to 0
		*/
		int num_bytes = 0;
		if((num_bytes = recvfrom(s, recvBuf, BUFSIZE-1, 0,
			(struct sockaddr *)&recvfrom_addr, &from_size)) < 0)
		{
			/*error*/
			fprintf(stderr, "errno: %d", errno);
			exit(1);
		}

		/*message was received */
		if(num_bytes > 0)
		{
			printf("Message received!\n");
			recvBuf[num_bytes] = '\0';
			printf("%s\n", recvBuf);

			char *tokens;

			tokens = strtok(recvBuf, " ,<>\n");

			/* parse and handle the insert function */
			if(strcmp(tokens, "insert") == 0)
			{
				if(i<MAXKEYS)
				{
					/* advance token */
					tokens = strtok(NULL, " ,<>\n");
					strcpy(keys[i], tokens);
					tokens = strtok(NULL, " ,<>\n");
					strcpy(values[i], tokens);

					msg = "<key, value> pair successfully inserted\n";
					len = strlen(msg);
					bytes_sent = sendall(s, msg, len, 0,
						(struct sockaddr *)&recvfrom_addr);

					i++;
				}
				else
				{
					msg = "insert failed, list full\n";
					len = strlen(msg);
					bytes_sent = sendall(s, msg, len, 0,
						(struct sockaddr *)&recvfrom_addr);
				}
			} /* parse/handle read <key> */
			else if(strcmp(tokens, "read") == 0)
			{
				int j;
				/* wipe the buffers */
				memset(reply, 0, sizeof(reply));
				memset(message, 0, sizeof(message));

				/* advance to the key */
				tokens = strtok(NULL, " ,<>\n");

				for(j=0; j<i; j++)
				{
					if(strcmp(tokens, keys[j]) == 0)
					{
						sprintf(message, "\n--\nKey: %s\nValue: %s\n--\n",
							keys[j], values[j]);
						strcpy(reply, message);

						break;
					}
				}

				/* key was found */
				if(j<i)
				{
					len = strlen(reply);
					bytes_sent = sendall(s, reply, len, 0,
						(struct sockaddr *)&recvfrom_addr);
				}
				else
				{
					/* failed */
					msg = "key not found!\n";
					len = strlen(msg);
					bytes_sent = sendall(s, msg, len, 0,
						(struct sockaddr *)&recvfrom_addr);
				}
			}/* parse and handle readall */
			else if(strcmp(tokens, "readall") == 0)
			{
				int j;

				/* wipe the buffers */
				memset(reply, 0, sizeof(reply));
				memset(message, 0, sizeof(message));

				strcpy(reply, "\n--\n");

				len = strlen(reply);
				bytes_sent = sendall(s, reply, len, 0,
						(struct sockaddr *)&recvfrom_addr);

				/* create a string for all key value pairs */
				for(j=0; j<i; j++)
				{
					sprintf(message, "Key: %s\nValue: %s\n--\n",
						keys[j], values[j]);
					strcpy(reply, message);

					/* send each set individually */
					len = strlen(reply);
					bytes_sent = sendall(s, reply, len, 0,
						(struct sockaddr *)&recvfrom_addr);
				}
			} /* parse/handle delete <key> */
			else if(strcmp(tokens, "delete") == 0)
			{
				int j;

				/* advance to the key */
				tokens = strtok(NULL, " ,<>\n");

				for(j=0; j<i; j++)
				{
					if(strcmp(tokens, keys[j]) == 0)
					{
						int k;

						for(k=j; k<i;k++)
						{
							strcpy(keys[k], keys[k+1]);
							strcpy(values[k], values[k+1]);
						}

						i--;
						/* clear the last entry to be safe */
						strcpy(keys[i], "\0");
						strcpy(values[i], "\0");

						/* j get's set to -1 to easily tell the next
						part that a match was found, this requires
						special attention because i was just decremented
						to reflect the new size of the array */
						j=-1;
						break;
					}
				}

				/* key was found */
				if(j<i)
				{
					msg = "key/value pair successfully deleted\n";
					len = strlen(msg);
					bytes_sent = sendall(s, msg, len, 0,
						(struct sockaddr *)&recvfrom_addr);
				}
				else
				{
					/* failed */
					msg = "key not found!\n";
					len = strlen(msg);
					bytes_sent = sendall(s, msg, len, 0,
						(struct sockaddr *)&recvfrom_addr);
				}
			}
			else
			{
				/* failed */
				msg = "unrecognized command!\n";
				len = strlen(msg);
				bytes_sent = sendall(s, msg, len, 0,
					(struct sockaddr *)&recvfrom_addr);
			}
		}

	}

	close(s);

	return 0;
}